﻿namespace StyleCop.CSharp.JslOrderingRules
{
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    [TestClass]
    public class TestMembersMustBeInAlphabeticalOrder : TestBase<RuleAnalyzer>
    {
        #region Constructors
        /// <summary>
        /// Initializes a new instance of the <see cref="TestMembersMustBeInAlphabeticalOrder"/> class.
        /// </summary>
        public TestMembersMustBeInAlphabeticalOrder()
            : base(RuleAnalyzer.MembersMustBeInAlphabeticalOrder)
        {
        }
        #endregion Constructors

        /* TODO: Implicitly named interface methods aren't being sorted correctly. */

        [TestMethod]
        public void No_members_so_no_violations()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    public class X
                    {
                    }
                }");
        }

        [TestMethod]
        public void Members_in_order_so_no_violations()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    public class X
                    {
                        private bool a;
                        private bool b;
                    }
                }");
        }

        [TestMethod]
        public void Members_out_of_order_so_violations()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    public class X
                    {
                        private bool b;
                        private bool a;
                    }
                }",
                new ViolationInfo(5, "The 'field b' should be after the 'field a'."));
        }

        [TestMethod]
        public void Multiple_violations_for_a_member_type_are_shown()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    public class X
                    {
                        private bool c;
                        private bool b;
                        private bool a;
                        private bool d;
                    }
                }",
                new ViolationInfo(6, "The 'field b' should be after the 'field a'."),
                new ViolationInfo(5, "The 'field c' should be after the 'field b'."));
        }

        [TestMethod]
        public void Access_modifiers_are_separated()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    public class X
                    {
                        private bool d;
                        private bool a;
                        public bool c;
                        public bool b;
                    }
                }",
                new ViolationInfo(5, "The 'field d' should be after the 'field a'."),
                new ViolationInfo(7, "The 'field c' should be after the 'field b'."));
        }

        [TestMethod]
        public void Comparisons_are_case_insensitive()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    public class X
                    {
                        private bool a;
                        private bool B;
                        private bool c;
                        private bool D;
                    }
                }");
        }

        [TestMethod]
        public void Classes_are_ordered()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    public class B
                    {
                    }

                    public class a
                    {
                    }
                }",
                new ViolationInfo(3, "The 'class B' should be after the 'class a'."));
        }

        [TestMethod]
        public void Delegates_are_ordered()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    private delegate void B();
                    private delegate void a();
                }",
                new ViolationInfo(3, "The 'delegate B' should be after the 'delegate a'."));
        }

        [TestMethod]
        public void Enums_are_ordered()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    private enum b { };
                    private enum a { };
                }",
                new ViolationInfo(3, "The 'enum b' should be after the 'enum a'."));
        }

        [TestMethod]
        public void Events_are_ordered()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    private class A
                    {
                        private event EventHandler b;
                        private event EventHandler a;
                    }
                }",
                new ViolationInfo(5, "The 'event b' should be after the 'event a'."));
        }

        [TestMethod]
        public void Fields_are_ordered()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    private class A
                    {
                        private int b;
                        private int a;
                    }
                }",
                new ViolationInfo(5, "The 'field b' should be after the 'field a'."));
        }

        [TestMethod]
        public void Fields_are_ordered_by_readonly()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    private class A
                    {
                        private readonly bool d;
                        private readonly bool a;
                        private bool c;
                        private bool b;
                    }
                }",
                new ViolationInfo(5, "The 'field d' should be after the 'field a'."),
                new ViolationInfo(7, "The 'field c' should be after the 'field b'."));
        }

        [TestMethod]
        public void Interfaces_are_ordered()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    private interface B
                    {
                    }

                    private interface A
                    {
                    }
                }",
                new ViolationInfo(3, "The 'interface B' should be after the 'interface A'."));
        }

        [TestMethod]
        public void Methods_are_ordered()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    private class Class
                    {
                        private void B()
                        {
                        }

                        private bool a(int param1)
                        {
                            return true;
                        }
                    }
                }",
                new ViolationInfo(5, "The 'method B' should be after the 'method a'."));
        }

        [TestMethod]
        public void Namespaces_are_ordered()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    namespace B
                    {
                    }

                    namespace A
                    {
                    }
                }",
                new ViolationInfo(3, "The 'namespace B' should be after the 'namespace A'."));
        }

        [TestMethod]
        public void Properties_are_ordered()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    public class MyClass
                    {
                        public bool B { get; set; }
                        public bool A { get; set; }
                    }
                }",
                new ViolationInfo(5, "The 'property B' should be after the 'property A'."));
        }

        [TestMethod]
        public void Structs_are_ordered()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    public struct B
                    {
                    }

                    public struct a
                    {
                    }
                }",
                new ViolationInfo(3, "The 'struct B' should be after the 'struct a'."));
        }

        [TestMethod]
        public void Statics_are_a_subset()
        {
            this.TestSource(
                @"namespace MyNamespace
                {
                    public class X
                    {
                        private static bool b;
                        private bool a;
                    }
                }");
        }
    }
}